﻿using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;

namespace Zavand.Web.Mvc.Manana.Framework
{
    public static class BaseExtensions
    {
        public const string EncTypeAttributeName = "enctype";
        public static MvcHtmlString ActionLink<T>(this HtmlHelper helper, string linkText, BaseRoute currentRoute, T r, object htmlAttributes = null) where T : BaseRoute
        {
            var uh = new UrlHelper(helper.ViewContext.RequestContext);
            var url = uh.RouteUrl(currentRoute, r);

            var attributes = "";
            if (htmlAttributes != null)
            {
                var d = new RouteValueDictionary(htmlAttributes);
                foreach (var k in d)
                {
                    attributes += String.Format(" {0}=\"{1}\"", k.Key, k.Value);
                }
            }
            return MvcHtmlString.Create(String.Format("<a href=\"{0}\"{2}>{1}</a>", url, linkText, attributes));
        }
        public static MvcHtmlString ActionLinkClone<T>(this HtmlHelper helper, string linkText, T currentRoute, Action<T> action, object htmlAttributes = null) where T : BaseRoute, new()
        {
            var r = currentRoute.Clone(action);
            return ActionLink(
                helper,
                linkText,
                null, // No need to follow context as exact clone was created
                r,    // We created a copy of existing route. No need to follow context.
                htmlAttributes
                );
        }
        public static void MapRoute<T>(this RouteCollection routes, bool isLocalizationSupported) where T : BaseRoute, new()
        {
            var r = new T();
            if (isLocalizationSupported)
            {
                routes.MapRoute(r.GetNameLocalized(), r.GetUrlLocalized(), r.GetDefaults(), r.GetConstraintsLocalized(), r.GetNamespaces());
            }
            routes.MapRoute(r.GetName(), r.GetUrl(), r.GetDefaults(), r.GetConstraints(), r.GetNamespaces());
        }
        public static void MapRoute<T>(this AreaRegistrationContext context, bool isLocalizationSupported) where T : BaseRoute, new()
        {
            var r = new T();
            if (isLocalizationSupported)
            {
                context.MapRoute(r.GetNameLocalized(), r.GetUrlLocalized(), r.GetDefaults(), r.GetConstraintsLocalized(), r.GetNamespaces());
            }
            context.MapRoute(r.GetName(), r.GetUrl(), r.GetDefaults(), r.GetConstraints(), r.GetNamespaces());
        }

        public static String RouteUrl<T>(this UrlHelper u, BaseRoute currentRoute, T r) where T : BaseRoute
        {
            if (currentRoute != null)
                r.FollowContext(currentRoute);
            var domain = r.GetDomain();

            // ------------------------
            // Route local url
            // ------------------------
            if (String.IsNullOrEmpty(domain))
            {
                var rd = new RouteValueDictionary(r);
                if (String.IsNullOrEmpty(r.Area))
                {
                    rd.Remove("Area");
                }
                if (String.IsNullOrEmpty(r.Locale))
                {
                    rd.Remove("Locale");
                }
                return u.RouteUrl(
                    String.IsNullOrEmpty(r.Locale) ? r.GetName() : r.GetNameLocalized(),
                    rd
                    );
            }

            // ------------------------
            // Route external domain
            // ------------------------
            var protocol = r.GetProtocol();
            var port = r.GetPort();

            // Determine required protocol
            if (protocol == BaseRoute.UrlProtocol.Inherited)
            {
                // Try to get protocol from current context
                try
                {
                    if (u.RequestContext.HttpContext.Request.Url != null &&
                        !Enum.TryParse(u.RequestContext.HttpContext.Request.Url.Scheme, true, out protocol))
                    {
                        throw new Exception(); // Protocol can't be parsed. Internal exception.
                    }
                }
                catch // In case if anything of following is null: u.RequestContext.HttpContext.Request.Url
                {
                    protocol = BaseRoute.UrlProtocol.Http;
                }
            }
            var protocolUrl = protocol.ToString().ToLower();

            // Determine required port
            var porturl = "";
            if (protocol == BaseRoute.UrlProtocol.Http && port == BaseRoute.DefaultHttpPort)
                port = 0;
            if (protocol == BaseRoute.UrlProtocol.Https && port == BaseRoute.DefaultHttpsPort)
                port = 0;

            if (port != 0)
                porturl = String.Format(":{0}", port);

            var path = r.GetUrl();
            if (!String.IsNullOrEmpty(path))
                path = "/" + path;

            return String.Format("{0}://{1}{2}{3}", protocolUrl, domain, porturl, path);
        }

        public static String RouteUrlClone<T>(this UrlHelper u, T currentRoute, Action<T> action) where T : BaseRoute, new()
        {
            var r = currentRoute.Clone(action);
            return RouteUrl(u, null, r); // We created a copy of existing route. No need to follow context.
        }

        public static MvcForm BeginForm<TRoute>(this HtmlHelper htmlHelper, TRoute route, FormMethod method = FormMethod.Post, FormEncodingType enctype = FormEncodingType.None, object htmlAttributes = null) 
            where TRoute : BaseRoute
        {
            var attr = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            if (enctype != FormEncodingType.None)
            {
                var enctypeValue = "";
                switch (enctype)
                {
                    case FormEncodingType.MultipartFormData:
                        enctypeValue = "multipart/form-data";
                        break;
                }
                if (!attr.ContainsKey(EncTypeAttributeName))
                {
                    attr.Add(EncTypeAttributeName, enctypeValue);
                }
            }
            else if (attr.ContainsKey(EncTypeAttributeName))
            {
                attr.Remove(EncTypeAttributeName);
            }

            return htmlHelper.BeginForm(
                route.Area,
                route.Controller,
                new RouteValueDictionary(route),
                method,
                attr
                );
        }
    }
}
